<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Yiven <vance_720@foxmail.com>
// +----------------------------------------------------------------------

// 应用公共文件
/**
 * 字符串加密、解密函数
 * @param  string  $string    加密、解密前的字符串
 * @param  string  $operation ENCODE为加密，DECODE为解密，可选参数，默认为ENCODE
 * @param  string  $key       密钥：数字、字母、下划线
 * @param  integer $expiry    过期时间
 * @return string             加密、解密后的字符串
 */
function encry_code($string, $operation = "ENCODE", $key = "", $expiry = 0) {
    $ckey_length = 4;
    $key = md5($key != "" ? $key : config("encry_key"));
    $keya = md5(substr($key, 0, 16));
    $keyb = md5(substr($key, 16, 16));
    $keyc = $ckey_length ? ($operation == "DECODE" ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : "";

    $cryptkey = $keya . md5($keya . $keyc);
    $key_length = strlen($cryptkey);

    $string = $operation == "DECODE" ? base64_decode(strtr(substr($string, $ckey_length), "-_", "+/")) : sprintf("%010d", $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
    $string_length = strlen($string);

    $result = "";
    $box = range(0, 255);

    $rndkey = array();
    for ($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    }

    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }

    for ($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }

    if ($operation == "DECODE") {
        if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
            return substr($result, 26);
        } else {
            return "";
        }
    } else {
        return $keyc . rtrim(strtr(base64_encode($result), "+/", "-_"), "=");
    }
}

/**
 * 数组转置，矩阵转置
 * @param array $arr 要转置的数组,必须是二维数组
 * @result array $result_arr 转置后的数组
 */
function array_turn($arr, $result_arr=array())
{
    if(count($arr) == count($arr, 1)){
        return $arr;// 一维数组,原样返回
    }
    foreach ($arr as $key => $value) {
        foreach ($value as $k => $v) {
            $result_arr[$k][] = $v;
        }
    }
    return $result_arr;
}

/**
 * array to jsonstring 支持多维数组
 * @param  [array] $arr 数组
 * @return [string] $str json字符串
 */
function arr_to_jsonstr($arr)
{
    if(is_array($arr)){
        if(count($arr) == count($arr, 1)){// COUNT_RECURSIVE或1，表示递归计数
            $symbol = ["pre"=>"{", "suf"=>"}"];// 一维数组
        }else{
            $symbol = ["pre"=>"[{","suf"=>"}]"];// 多维数组
        }
        $str = $symbol["pre"];// 起始
        $len = count($arr);// 一维数组长度
        $i = 0;// 计数器
        foreach ($arr as $key => $value) {
            if(is_array($value)){
                $str .= "\"" . $key . "\":" . arr_to_jsonstr($value);// 数组递归转换
            }else{
                $str .= "\"".$key."\":\"".$value."\"";// 非数组，直接拼接
            }
            if($i + 1 < $len){$str .= ", ";}
            $i++;// 计数器自增
        }
        $str .= $symbol["suf"];// 结束
        return $str;
    }else{
        return $arr;
    }
}

/**
 * 检查目录是否存在，不存在则创建之
 * 说明：得到路径后，先判断是否已是一个有效的文件目录，如是则返回，结束程序。
 * 如果不是(这里使用了or作选择性的条件，只要满足其中一个条件即可)，
 * 则递归调用自身，并且传入的路径减少一级，
 * 以回到父级目录中先创建上级目录，再创建下级目录。
 * PHP5.0+新增一个参数recursive，设置为true可以递归创建目录，Windows下$mode参数将被忽略
 */
function mkdirs($dir, $mode=0777, $recursive=true){
    return is_dir($dir) or (mkdirs(dirname($dir)) and mkdir($dir, $mode,$recursive));
}

/**
 * 记录日志
 * @param [string] $filename [日志文件名，默认：当天日期]
 * @param [json] $msgstr  [日志内容，默认：无]
 * @param [string] $filesuffix [日志文件后缀名，默认：.log]
 */
function record_log($msgstr, $filename=null, $filesuffix=".log", $dir="log")
{
    // 设置目录名称
    $dir .= "/".date("Ym");// 增加子目录(按月份新增格式：201907)
    mkdirs($dir);   // 如果目录不存在，将以递归方式创建该目录

    // 设置文件名称
    $date = date('d');// 获取日期
    if(empty($filename)){
        $filename = $date; // 设置默认文件名
    }else{
        $filename = $filename.$date;// 为文件名加上日期，防止单文件过大导致程序超时
    }

    // 写入日志，使用FILE_APPEND追加日志，而不覆盖之前的内容
    file_put_contents($dir."/".$filename.$filesuffix, $msgstr."\t".date("Y-m-d H:i:s", time())."\r\n", FILE_APPEND);//记录日志
}

/**
 * form-post跳转方式发起请求
 * @param  string $url  要请求的地址
 * @param  array $data 要发送的数据，key-name,value-value
 * @return echo string/html    输出html到页面，实现请求发起并跳转到请求页面
 */
function form_post($url, $data, $title="请求正在发送...")
{
    $str = "<!DOCTYPE html><html><meta charset='utf-8'><title>";
    $str .= $title;
    $str .= "</title><body onload='document.pay.submit()'>";
    $str .= '<form id="pay" name="pay" method="post" action="' . $url . '" >';
    foreach ($data as $key => $value) {
        $str .= '<input type="hidden" name="' . $key . '" value="' . $value . '">';
    }
    $str .= '</form></body></html>';
    exit($str);
}

/**
 * cURL方式发起POST请求,
 * @param  string  $url     服务器地址
 * @param  string/array  $data    要发送到服务器的数据
 * @param  string  $header  头信息，格式：array('Content-type: text/plain', 'Content-length: 100')
 * @param  string  $referer 请求来源，HTTP请求头中"referer"的内容，告诉服务器我是从哪里来的
 * @return string  $output  服务器返回信息
 */
function http_request($url, $data = null, $method = "post", $header = null, $referer = null)
{
    $curl = curl_init();// 创建一个cURL资源
    curl_setopt($curl, CURLOPT_URL, $url);// 设置URL
    curl_setopt($curl, CURLOPT_TIMEOUT, 30);// 设置cURL超时时间，单位：秒

    // 设置http头信息
    if ($header) {
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    }

    // 设置在HTTP请求头中"referer"的内容
    if ($referer) {
        curl_setopt($curl, CURLOPT_REFERER, $referer);
    }

    // 检查数据包是否为空
    if (!empty($data)){
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    }else{
        curl_setopt($curl, CURLOPT_POST, FALSE);
    }

    // 检查url是否是https请求，stripos()返回字符串a在字符串b中首次出现的下标(从0开始)，否则返回false
    if(stripos($url, 'https://') !== FALSE){
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // 跳过证书来源检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
    }
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);// 提高curl访问速度
    $output = curl_exec($curl);
    /*
    if($output){
        file_put_contents('./curl.log', '---------info---------'."\r\n", FILE_APPEND);
        file_put_contents('./curl.log', '---------error---------'."\r\n", FILE_APPEND);
        file_put_contents('./curl.log', '---------response---------'."\r\n", FILE_APPEND);
        print_r($output);
        die($error);
    }
    */
    curl_close($curl);
    return $output;
}

/**
 * 
 */
function get_host_url(){
    $ssl = (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on')||(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') ? true : false;// 是http请求还是https请求
    $sp = strtolower($_SERVER['SERVER_PROTOCOL']);// 请求页面时通信协议的名称和版本
    // strpos(string, find, start) 函数查找字符串a在字符串b中第一次出现的位置
    // substr(string, start, length)
    $protocol = substr($sp, 0, strpos($sp, '/')) . (($ssl) ? 's' : '');// 构造请求类型
    $port  = $_SERVER['SERVER_PORT'];// 获取端口
    $port = ((!$ssl && $port=='80') || ($ssl && $port=='443')) ? '' : ':'.$port;
    $host = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : isset($_SERVER['HTTP_HOST']) ? explode(':', $_SERVER['HTTP_HOST'])[0] : explode(":", $_SERVER['SERVER_NAME'])[0];
    return $protocol . '://' . $host . $port;
}